pub fn learn_patterns() {
  //_learn_if_let();
  //_learn_while_let();
  //_learn_for_pattern();
  //_learn_fn_args_pattern();
  //_learn_match_variable();
  //_learn_inclusive_range();
  //_learn_destruct_struct();
  //learn_destruct_enum();
  _learn_at();
  _learn_at1();
}

// 可反驳模式
// 不可反驳模式

// 所有可能会用到模式的位置
// match分支
// 模式常用的一个位置是match表达式的分支.形式上match表达式由match关键字,用于匹配的值和一个或多个分支
// 构成,这些分支包含 一个模式 和 在值匹配分支的模式时 运行的表达式
/*
match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}
*/
// match x {
//  None=>None,
//  Some(i)=>Some(i+1)
// }

// if let 条件表达式
// 主要用于编写等同于只关心一个情况的match语句的简写.可以对应一个可选的带有代码的else 在 if let中的
// 模式不匹配时运行.
fn _learn_if_let() {
  let fav: Option<&str> = None;
  //let fav:Option<&str> = Some("red");
  let is_tuesday = false;
  let age: Result<u8, _> = "34".parse();
  if let Some(color) = fav {
    println!("Using your fav color, {color}, as the background");
  } else if is_tuesday {
    println!("Tuesday is green day!");
  // 覆盖变量
  // if let Ok(age)=age引入了一个新的覆盖变量age,它包含Ok成员的值.这意味着if age>30条件需要位于
  // 这个代码块内部; 不能将两个条件组合为if let Ok(age)=age && age>30
  // 编译报错: `let` expressions in this position are unstable
  // see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
  // 因为我们希望与30进行比较的被覆盖的age直到大括号开始的新的作用域才是有效的
  //}else if let Ok(age) = age && age>30 {
  } else if let Ok(age) = age {
    if age > 30 {
      println!("Using purple as the background color");
    } else {
      println!("Using orange as the background color");
    }
  } else {
    println!("Using blue as the background color");
  }
  // if let 表达式的缺点在于其穷尽性没有为编译器所检查,而match表达式则检查了.如果去掉最后的else
  // 块而遗漏处理一些情况,编译器也不会警告这类可能的逻辑错误.
}

// while let 条件循环
// 允许只要模式匹配就一直进行while循环
fn _learn_while_let() {
  let mut stack = Vec::new();
  stack.push(1);
  stack.push(2);
  stack.push(3);
  while let Some(top) = stack.pop() {
    println!("{}", top);
  }
  println!("{:?}", stack.pop()); // out: None
}

// for 循环
// for循环中,模式是for关键字直接跟随的值,比如for x in y中的x.
fn _learn_for_pattern() {
  let v = vec!['a', 'b', 'c'];
  // 使用 enumerate 方法适配一个迭代器来产生一个值和其在迭代器中的索引,它们位于一个元组中.
  // 第一个产生的值是元组(0,'a').当这个值匹配模式(idx,val)时,idx将会是0而val将会是'a'
  for (idx, val) in v.iter().enumerate() {
    println!("{} is at idx:{}", val, idx);
  }
}

// let语句
fn _learn_let_pattern() {
  let x = 5;
  // let PATTERN = EXPRESSION;
  // 将表达式与模式比较,并为任何找到的名称赋值.
  // x是一个代表"将匹配到的值绑定到变量x"的模式.同时因为名称x是整个模式,
  // 这个模式实际上等于"将任何值绑定到变量x,不管值是什么".

  // 使用模式解构元组并一次创建三个变量
  // 这里将一个元组与模式匹配.rust会比较值(1,2,3)与模式(x,y,z)并发现此值匹配这个模式.在这个例子中,将
  // 会把1绑定到x,2绑定到y,并将3绑定到z.你可以将这个元组模式看做是将三个独立的变量模式结合在一起.
  let (x, y, z) = (1, 2, 3);

  // 如果模式中元素的数量不匹配元组中元素的数量,则整个类型不匹配,并会得到一个编译时错误.
  // let (x,y)=(1,2,3);
  // 为了修复这个错误,可以用_或..来忽略元组中一个或多个值
  let (x, ..) = (1, 2, 3);
  let (a, b, ..) = (1, 2, 3);
  let (a, b, _) = (1, 2, 3);

  // 如果问题是模式中有太多的变量,则解决方法是去掉过多的变量使变量数与元组中的元素数相等.
}

// 函数参数
// 函数参数也可以是模式
fn _learn_fn_args_pattern() {
  // x就是一个模式.类似于之前的let,可以在函数参数中匹配元组
  fn foo(x: i32) {}

  fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({},{})", x, y);
  }
  let point = (3, 5);
  print_coordinates(&point);
}

// 闭包参数列表

// Refutability(可反驳性): 模式是否会匹配失效
// 模式有两种形式:refutable(可反驳的)和irrefutable(不可反驳的).能匹配任何传递的可能值的模式被称为
// 是不可反驳的(irrefutable).一个例子就是let x=5; 语句中的x,因为x可以匹配任何值,所以不可能会失败.
// 对某些可能的值进行匹配会失败的模式被称为是可反驳的(refutable).一个这样的例子便是if let Some(x)=value
// 表达式中的Some(x); 如果变量value中的值是None而不是Some,那么Some(x)模式不能匹配.

// 函数参数,let语句和for循环只能接受不可反驳的模式,因为当值不匹配时,程序无法进行有意义的操作.
//  if let和while let表达式两种模式都可接受,但编译器会对不可反驳的模式发出警告,
// 因为根据定义它们旨在处理可能的失败:条件表达式的功能在于它能够根据成功或失败来执行不同的操作.

// 一个在rust邀请不可反驳模式的地方使用可反驳模式以及相反情况的例子.
fn _learn_refutable0() {
  let opt = Some(1);
  // let Some(x) = opt;
  if let Some(x) = opt {}
  if let x = 5 {}
}

// 所有的模式语法
// 匹配字面值
fn learn_match_literal() {
  let x = 1;
  match x {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("any"),
  }
}

// 匹配命名变量
// 命名变量是匹配任何值的不可反驳模式.match会开始一个新的作用域,match表达式中作为模式的一部分声明的变量
// 会覆盖match结构之外的同名变量,与所有变量一样.
fn _learn_match_variable() {
  // let x = Some(5);
  let x = Some(50);
  //let x = None;
  let y = 10;
  // 运行代码,分析..
  match x {
    // 第一个匹配分支的模式并不匹配x中定义的值(50!=5),所以代码继续执行
    Some(50) => println!("Got 50"),
    // 第二个匹配分支中的模式引入了一个新变量y,它会匹配任何Some中的值.因为我们在match表达式的
    // 新作用域中,这是一个新变量,而不是开头生命为值10的那个y.这个新的y绑定会匹配任何Some中的值,
    // 在这里是x中的值.因此这个y绑定了x中Some内部的值.这个值是5,所以这个分支表达式会执行
    Some(y) => println!("Matched, y={y}"),
    // 如果x的值是None而不是Some(5),前面两个分支的模式不会匹配,就会匹配到_.这个分支的模式中
    // 没有引入变量x,所以此时表达式中的x会是外部没有被覆盖的x.
    _ => println!("Default case, x={:?}", x),
  } //match表达式执行结束,作用域也就结束了,下面的x=Some(5),y=10
  println!("at the end:x={:?}, y={y}", x);
}

// 多个模式
// 在match表达式中,可以使用|语法匹配多个模式,它代表或(or)运算符模式
fn _learn_multi_or() {
  let x = 1;
  match x {
    1 | 2 => println!("one or two"),
    3 => println!("three"),
    _ => println!("any"),
  }
}

// 通过..=匹配值的范围
// ..=语法允许你匹配一个闭区间范围内的值.
// 只支持数字和字符
fn _learn_inclusive_range() {
  let x = 2;
  match x {
    // 只支持闭区间
    // 0..1=>println(""),
    1..=3 => println!("1~3"),
    4..=6 | 10..=12 => println!("4~6 or 10~12"),
    _ => println!("something else"),
  }
  let y = 'k';
  match y {
    'a'..='e' => println!("a~e"),
    'g'..='i' | 'l'..='z' => println!("g~i or l~z"),
    _ => println!("something else"),
  }
}

// 解构并分解值
// 模式可以解构结构体,枚举和元组
// 解构结构体
struct Point {
  x: i32,
  y: i32,
}

fn _learn_destruct_struct() {
  let p = Point { x: 0, y: 7 };
  // 模式中的变量名不必与结构体中的字段名一致
  let Point { x: a, y: b } = p;
  assert_eq!(0, a);
  assert_eq!(7, b);

  // 一般变量名会匹配字段名,以便于理解变量来自于哪个字段.
  // 按上述解构会有很多冗余,因此支持简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称
  let Point { x, y } = p;
  assert_eq!(0, x);
  assert_eq!(7, y);

  // 也可以使用字面值作为结构体模式的一部分进行解构,而不是为所有的字段创建变量.这允许我们测试一些
  // 字段为特定值的同时创建其他字段的变量
  match p {
    // 此模式当y匹配0时,创建变量x
    Point { x, y: 0 } => println!("On the x axis at {x}"),
    // 此模式当x匹配0时,创建变量y
    Point { x: 0, y } => println!("On the y axis at {y}"),
    // 此模式当x,y都不指定匹配条件时,同时创建变量x,y
    Point { x, y } => println!("On neither axis: {x}, {y}"),
  }
}

// 解构枚举
// 解构枚举的模式需要对应枚举所定义的储存数据的方式
enum Message {
  Quit,
  Move { x: i32, y: i32 },
  Write(String),
  ChangeColor(i32, i32, i32),
}

fn learn_destruct_enum() {
  let msg = Message::ChangeColor(0, 160, 255);
  match msg {
    // 没有任何数据的枚举成员,不能进一步解构其值,只能匹配其字面值Message::Quit,因此模式中没有任何变量
    Message::Quit => {
      println!("Quit");
    }
    // 类结构体枚举成员,可以采用类似匹配结构体的模式
    Message::Move { x, y } => {
      println!("Move {x}, {y}");
    }
    // 模式解构类似元组
    Message::Write(text) => {
      println!("Write {text}");
    }
    // 模式解构类似元组
    Message::ChangeColor(r, g, b) => {
      println!("ChangeColor {r},{g},{b}");
    }
  }
}

// 解构嵌套的结构体和枚举
enum Color {
  Rgb(i32, i32, i32),
  Hsv(i32, i32, i32),
}
enum Info {
  ColorInfo(Color),
}
fn learn_destruct_enum_nest() {
  let msg: Info = Info::ColorInfo(Color::Hsv(0, 160, 255));
  match msg {
    // 没有任何数据的枚举成员,不能进一步解构其值,只能匹配其字面值Message::Quit,因此模式中没有任何变量
    Info::ColorInfo(Color::Rgb(r, g, b)) => {
      println!("ColorInfo rgb:{r},{g},{b}");
    }
    Info::ColorInfo(Color::Hsv(h, s, v)) => {
      println!("ColorInfo hsv:{h},{s},{v}");
    }
    _ => (),
  }
}

// 解构结构体和元组
// 复杂的混合形式
// let ((feet,inches), Point{x,y})=((3,10), Point{x:3,y:-10})

// 忽略模式中的值

fn foo(_: i32, y: i32) {
  println!("{y}");
}

// 大部分情况当你不再需要特定函数参数时,最好修改签名不再包含无用的参数.在一些情况下忽略函数参数会
// 变得特别有用,比如实现trait时,当你需要特定类型签名但是函数实现并不需要某个参数时.这样可以避免一个
// 存在未使用的函数参数的编译警告,就跟使用命名参数一样

// 使用嵌套的_忽略部分值
fn learn_ignore_part() {
  let mut setting = Some(5);
  let new_setting = Some(10);
  match (setting, new_setting) {
    (Some(_), Some(_)) => {
      println!("Cant overwrite");
    }
    _ => {
      setting = new_setting;
    }
  }
}

// 通过在名字前以一个_开头来忽略为使用的变量
// 用..忽略剩余的值

// 匹配守卫提供的额外的条件
fn _learn_match_guard0() {
  let num = Some(4);
  match num {
    Some(x) if x % 2 == 0 => println!("num is even:{}", x),
    Some(x) => println!("num {} is odd", x),
    None => (),
  }
  // 匹配守卫的缺点:编译器不会尝试为包含匹配守卫的模式检查穷尽性

  // 匹配守卫解决模式中变量覆盖的问题
  let x = Some(5);
  let y = 10;
  match x {
    Some(50) => println!("Got 50"),
    // Some(y) =>println!("Matched, y={y}"),
    // 第二个匹配分支中的模式不会引入一个覆盖外部y的新变量y,因此匹配守卫中可以使用外部的y.
    // 匹配守卫 if n==y 并不是一个模式所以没有引入新变量. 此时这里的y 正是外部的y 而不是
    // 新的覆盖变量y, 这样就可以通过比较n和y来表达寻找一个与外部y相同的值的概念了.
    Some(n) if n == y => println!("Matched, n={n}"),
    _ => println!("Default case, x={:?}", x),
  }
  println!("at the end:x={:?}, y={y}", x);

  let x = 4;
  let y = false;
  match x {
    // 也可以在匹配守卫中使用或运算符来指定多个模式,同时匹配守卫的条件会作用于所有的模式
    // 下一行中, if y 作用于 4,5和6,而不仅作用于6
    // 这个匹配条件表明此分支匹配x值为4,5或6,同时y为true的情况.
    4 | 5 | 6 if y => println!("yes"),
    // 匹配守卫与模式的优先级关系看起来像这样:
    // (4|5|6) if y => ...
    // 而不是
    // 4|5|(6 if y)
    _ => println!("no"),
  }
}

// @绑定
// at运算符(@)允许我们在创建一个存放值的变量的同时测试其值是否匹配模式.
// 使用@可以在一个模式中同时测试和保存变量值
enum Mesg {
  Hello { id: i32 },
}
fn _learn_at() {
  let msg = Mesg::Hello { id: 11 };
  match msg {
    Mesg::Hello {
      // 在此范围的值会绑定到id_variable中
      id: id_variable @ 3..=7,
    } => println!("{}", id_variable),
    Mesg::Hello {
      // 在此范围的值没有可绑定的变量
      id: 10..=12,
    } => println!("another range"),
    // 这里使用了结构体字段简写的语法,模式中的字段与新的变量名一样
    // Mesg::Hello {id:id}
    Mesg::Hello { id } => println!("id:{}", id),
  }
}

fn _learn_at1() {
  //let a@1..=3=2;

  if let a @ 1..=3 = 1 {
    println!("a:{}", a);
  } else {
    println!("other else");
  }
}
